/**
=blacksmith/ji - A vi like code editor

This module provides a vi like code editor for editing blacksmith files.

Ji has two modes, insert mode and command mode. The editor begins in
insert mode, indicated by a thin caret positioned between characters.
To switch from insert mode to command mode press [ESC]. Command mode
is indicated by a wide caret and the visibility of the command bar.

=Command mode

Switching back to insert mode

.a    append text, after the cursor
.A    append text, after end of line
.i    insert text, before the cursor
.I    insert text, before first non-whitespace character
.o    open new line below cursor in Insert Mode
.O    open new line above cursor in Insert Mode  

=BLACKSMITH.main

The initial open file can be set with the parameter f.

. URL:     /blacksmith/ji?f=/file.js
. blacksmith/term: blacksmith/ji file.js

*/



window.JI = {}


JI.container = document.body

JI.font = {
    size  : '14',
    width : undefined,
    height: undefined
}

JI.editel  = undefined

JI.text    = null

JI.lines   = 0

JI.command = {
    el    : undefined,
    input : undefined,
    info  : undefined,
    mode  : false
}

JI.ctrl = {
    el          : undefined,
    saveFile    : undefined,
    replace     : undefined,
    increaseFont: undefined,
    decreaseFont: undefined,
    style       : undefined
}

JI.underlay = {
    el        : undefined,
    gap       : [],
    operator  : {text: null, el: null},
    whitespace: {text: null, el: null},
    keyword   : {text: null, el: null},
    name      : {text: null, el: null},
    number    : {text: null, el: null},
    string    : {text: null, el: null},
    comment   : {text: null, el: null}
}

JI.ruler = {
    el        : null,
    text      : null,
    count     : null
}

JI.cursor = {
    el        : undefined,
    pos       : -1,
    indent    : undefined,
    offsetTop : 0
}

JI.insertmode = true

JI.editbuffer = ''

JI.tokens     = []

JI.colour_styles    = {
    false: { //dark colours style
        operator    : '#C19859',
        whitespace  : '#333333',
        keyword     : '#e06c1f',
        name        : '#FFFFFF',
        modulename  : '#C19859',
        number      : '#0086FF',
        string      : '#7D8349',
        comment     : '#f2f264',
        ruler       : '#222222',
        linenumber  : '#777777',
        lineborder  : '#555555',
        ctrlbarbg   : '#eeeeee',
        cmdbarbg    : '#eeeeee',
        bartext     : '#000000',
        background  : '#141414',
        cursorInsert: '#FFFFFF',
        cursorEdit  : '#AAAAAA'
    },
    true: { //light colours style
        operator    : '#804040',
        whitespace  : '#bbbbbb',
        keyword     : '#ff0000',
        name        : '#000000',
        modulename  : '#804040',
        number      : '#2020b9',
        string      : '#256a31',
        comment     : '#929204',
        ruler       : '#eeeeee',
        linenumber  : '#777777',
        lineborder  : '#999999',
        ctrlbarbg   : '#eeeeee',
        cmdbarbg    : '#eeeeee',
        bartext     : '#000000',
        background  : '#ffffff',
        cursorInsert: '#000000',
        cursorEdit  : '#AAAAAA'
    }
}

JI.current_style = true

JI.colours = JI.colour_styles[JI.current_style]

JI.editfile = undefined

JI.lastKeyType = 'keyup'

JI.a = 0
JI.b = 0
JI.c = 0

JI.pos = 0

JI.precursor = 0

JI.indent = 0

JI.ntokens = 0

JI.iteration = 0

JI.element = 0

JI.token = 0




JI.open = function(path) {
    /*
     * Opens the File at path for editing.
     */
    
    JI.editfile = BLACKSMITH.File(path)
    
    JI.editfile.read(function(read) {
        document.title = read.file.path
        JI.editel.value = read.file.content
        JI.editel.selectionStart = JI.editel.selectionEnd = 0
        JI.refreshDisplay()
        JI.ctrl.saveFile.style.opacity = '0.2'
    })

}



JI.save = function(callback) {
    /*
     * Writes contents of editarea to File and flushes to server.
     */
    
    if (typeof JI.editfile === 'undefined')
        returni
    
    JI.editfile.content = JI.editel.value;
    
    JI.iteration = 0
    var ntext = JI.command.info.textContent
    
    JI.editfile.flush(function(flush) {
        if (flush.hasOwnProperty('multiparts')) {
            JI.a = flush.multiparts
            return
        }
        if (flush.hasOwnProperty('multipart')) {
            JI.command.info.textContent = '' + ++JI.iteration + '/' + JI.a + '  ' + ntext
            return
        }
        if (flush.hasOwnProperty('success')) {
            JI.command.info.textContent = '-saved-  ' + ntext
            JI.ctrl.saveFile.style.opacity = '0.2'
        } else if (flush.hasOwnProperty('error'))
            JI.command.info.textContent = flush.error + '  ' + ntext
        
        if (typeof callback === 'function')
            callback(flush)
    })

}



JI.replace = function() {
    /*
     * Replaces with regexp pattern with a replacement string.
     */
    
    var pattern = prompt('a \u21AA b\nFind what:')
    
    if (pattern == null || pattern === '')
        return
    
    var replacement = prompt('a &#8618; b\nReplace "' + pattern + '" with:', pattern)
    
    if (replacement == null)
        return
    
    replacement = replacement.replace('\\n', '\n')
    
    JI.editel.undo = JI.editel.value
    JI.editel.value = JI.editel.value.replace(new RegExp(pattern, 'g'), replacement)
    JI.refreshDisplay()
    
}



JI.scrollToCursor = function() {
    /*
     * Scrolls view so cursor is visible.
     */
    
    JI.iteration = (document.documentElement.scrollTop || document.body.scrollTop )
    JI.precursor = window.innerHeight || document.documentElement.clientHeight
    
    if (JI.cursor.offsetTop < JI.iteration)
        JI.iteration = JI.cursor.offsetTop
    
    else if (JI.cursor.offsetTop + (JI.font.height * 3) > JI.iteration + JI.precursor)
        JI.iteration = JI.cursor.offsetTop + (JI.font.height * 3) - JI.precursor
    
    document.documentElement.scrollTop = JI.iteration
    document.body.scrollTop = JI.iteration

}



JI.refreshCursor = function() {
    /*
     * Updates cursor position.
     */
    
    JI.precursor = JI.editel.value.substring(0, JI.editel.selectionStart).replace(/[^\n]*\n/g, '\n')
    JI.indent = JI.precursor.replace(/\n/g, '').length
    
    if (JI.insertmode)
        JI.cursor.indent = JI.indent
    
    JI.cursor.offsetTop = JI.precursor.replace(/[^\n]/g, '').length * JI.font.height
    JI.cursor.pos = JI.editel.selectionStart
    JI.cursor.el.style.left = String(JI.indent * (JI.font.width - 2)) + 'px'
    JI.cursor.el.style.top = String(JI.cursor.offsetTop) + 'px'
    
    JI.scrollToCursor()
        
    JI.command.info.textContent = JI.cursor.pos + ':' + String(JI.editel.value.length)
    
    if (typeof JI.editfile === 'object')
        JI.command.info.textContent += '  ' + JI.editfile.path + '  '
    else
        JI.command.info.textContent += '  cmd  '

}



JI.refreshDisplay = function() {
    /*
     * Updates display.
     */
    
    if (JI.cursor.pos !== JI.editel.selectionStart)
        JI.refreshCursor()
    
    if (JI.text === JI.editel.value)
        return
    
    JI.text = JI.editel.value
    
    JI.ntokens = BLACKSMITH.tokenise(JI.text)
    
    JI.ruler.text = ''
    JI.ruler.count = 1
    JI.underlay.operator.text = ''
    JI.underlay.whitespace.text = ''
    JI.underlay.keyword.text = ''
    JI.underlay.name.text = ''
    JI.underlay.number.text = ''
    JI.underlay.string.text = ''
    JI.underlay.comment.text = ''
    
    JI.ruler.text += JI.ruler.count++ + '\n'
        
    // update the syntax highlights
    for (JI.iteration = 0; JI.iteration < JI.ntokens.length; JI.iteration++) {
        
        JI.token = JI.ntokens[JI.iteration]
        
        if (JI.token.type === 0) {
            // operator
            
            JI.underlay.operator.text += JI.token.value
            JI.underlay.whitespace.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.keyword.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.name.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.number.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.string.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.comment.text += JI.underlay.gap[JI.token.value.length]
            
        } else if (JI.token.type === 1) {
            // space
            
            JI.underlay.operator.text += JI.token.value
            JI.underlay.whitespace.text += JI.token.value.replace(/ /g, '\u00B7')
            JI.underlay.keyword.text += JI.token.value
            JI.underlay.name.text += JI.token.value
            JI.underlay.number.text += JI.token.value
            JI.underlay.string.text += JI.token.value
            JI.underlay.comment.text += JI.token.value
            
        } else if (JI.token.type === 2) {
            // newline
            for (JI.iter2 = 0; JI.iter2 < JI.token.value.length; JI.iter2++)
                JI.ruler.text += JI.ruler.count++ + '\n'
            JI.underlay.operator.text += JI.token.value
            JI.underlay.whitespace.text += JI.token.value.replace(/\n/g, '\u00B6\n')
            JI.underlay.keyword.text += JI.token.value
            JI.underlay.name.text += JI.token.value
            JI.underlay.number.text += JI.token.value
            JI.underlay.string.text += JI.token.value
            JI.underlay.comment.text += JI.token.value
            
        } else if (JI.token.type === 4) {
            // keyword
            
            JI.underlay.operator.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.whitespace.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.keyword.text += JI.token.value
            JI.underlay.name.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.number.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.string.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.comment.text += JI.underlay.gap[JI.token.value.length]
            
        } else if (JI.token.type === 5) {
            // name
            
            JI.underlay.operator.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.whitespace.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.keyword.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.name.text += JI.token.value
            JI.underlay.number.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.string.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.comment.text += JI.underlay.gap[JI.token.value.length]
            
        } else if (JI.token.type === 6) {
            // number
            
            JI.underlay.operator.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.whitespace.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.keyword.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.name.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.number.text += JI.token.value
            JI.underlay.string.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.comment.text += JI.underlay.gap[JI.token.value.length]
            
        } else if (JI.token.type === 7) {
            // string
            
            JI.underlay.operator.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.whitespace.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.keyword.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.name.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.number.text += JI.underlay.gap[JI.token.value.length]
            JI.underlay.string.text += JI.token.value
            JI.underlay.comment.text += JI.underlay.gap[JI.token.value.length]
            
        } else if (JI.token.type === 3) {
            // comment
            
            for (JI.iter2 = 0; JI.iter2 < JI.token.value.length; JI.iter2++) {
                if (JI.token.value[JI.iter2] === ' ') {
                    JI.underlay.whitespace.text += '\u00B7'
                    JI.underlay.operator.text += ' '
                    JI.underlay.keyword.text += ' '
                    JI.underlay.name.text += ' '
                    JI.underlay.number.text += ' '
                    JI.underlay.string.text += ' '
                    JI.underlay.comment.text += ' '
                } else if (JI.token.value[JI.iter2] === '\n') { 
                    JI.ruler.text += JI.ruler.count++ + '\n'
                    JI.underlay.whitespace.text += '\u00B6\n'
                    JI.underlay.operator.text += '\n'
                    JI.underlay.keyword.text += '\n'
                    JI.underlay.name.text += '\n'
                    JI.underlay.number.text += '\n'
                    JI.underlay.string.text += '\n'
                    JI.underlay.comment.text += '\n'
                } else {
                    JI.underlay.whitespace.text += ' '
                    JI.underlay.operator.text += ' '
                    JI.underlay.keyword.text += ' '
                    JI.underlay.name.text += ' '
                    JI.underlay.number.text += ' '
                    JI.underlay.string.text += ' '
                    JI.underlay.comment.text += JI.token.value[JI.iter2]
                }
                
            }
            
        }
        
    }
    

    JI.ruler.el.textContent = JI.ruler.text
    JI.underlay.operator.el.textContent = JI.underlay.operator.text
    JI.underlay.whitespace.el.textContent = JI.underlay.whitespace.text
    JI.underlay.keyword.el.textContent = JI.underlay.keyword.text
    JI.underlay.name.el.textContent = JI.underlay.name.text
    JI.underlay.number.el.textContent = JI.underlay.number.text
    JI.underlay.string.el.textContent = JI.underlay.string.text
    JI.underlay.comment.el.textContent = JI.underlay.comment.text
    

    JI.editel.rows = JI.ruler.count
    JI.editel.style.width = JI.underlay.whitespace.el.offsetWidth + 4 + 'px'
    
    JI.scrollToCursor()
    
    if (JI.ctrl.saveFile.style.opacity !== '1.0')
        JI.ctrl.saveFile.style.opacity = '1.0'

}



JI.editCount = function(key) {
    /*
     * Returns the buffered edit repeat count.
     */
    
    var re = new RegExp('.*?(\\d+)' + key + '$')

    return (re.test(JI.editbuffer)) ? parseInt(JI.editbuffer.replace(re, '$1'), 10) : 1

}



JI.insertMode = function(insert) {
    /*
     * Sets the mode to insert if true.
     */
    
    if (insert) {
        JI.cursor.el.style.background = 'none'
        JI.cursor.el.style.opacity = '1'
        JI.command.el.style.visibility = 'hidden'
        JI.insertmode = true
        JI.editbuffer = ''
        return
    }
    
    JI.cursor.el.style.background = JI.colours.cursorEdit
    JI.cursor.el.style.opacity = '.5'
    JI.command.input.style.display = 'none'
    JI.command.el.style.visibility = 'visible'
    JI.insertmode = false
    JI.editbuffer = ''

}



JI.commandInput = function(input) {
    /*
     * Sets the state of the command-line input.
     */
    
    if (input) {
        JI.command.input.style.display = 'inline'
        JI.command.input.value = ':'
        JI.command.input.focus()
        JI.command.input.selectionStart = JI.command.input.selectionEnd = 1
        JI.command.mode = true
        return
    }
    
    JI.command.input.value = ''
    JI.command.input.style.display = 'none'
    JI.command.mode = false
    JI.editel.focus()

}


JI.execute = function(cmd) {
    /*
     * Executes the specified command string.
     */
    
    var goto_re = /:([0-9]+|\$)/
    
    if (cmd === ':q') {

        window.open('', '_self', '')
        window.close()

    } else if (cmd === ':w') {

        JI.save()

    } else if (cmd === ':wq') {

        JI.save(function(m) {
            if (m.hasOwnProperty('success')) {
                window.open('', '_self', '')
                window.close()
            }
        })

    } else if (goto_re.test(cmd)) {

        var line = cmd.replace(goto_re, '$1')
        
        if (line === '$')
            line = JI.text.split('\n').length
        else
            line = parseInt(line)
        
        var offset = 0
        
        while (offset != -1 && line > 1) {
            offset = JI.text.indexOf('\n', offset) + 1
            line--
        }
        
        JI.editel.selectionStart = JI.editel.selectionEnd = offset
        JI.refreshCursor()

    }

}



JI.cursorToLineBegin = function() {
    /*
     * Moves to the cursor the first none space character of the line.
     */
    
    var pos = JI.text.lastIndexOf('\n', JI.editel.selectionStart - 1) + 1
    
    while (JI.text.charAt(pos) === ' ')
        pos++
    
    JI.cursor.indent = pos - JI.text.lastIndexOf('\n', pos)
    
    return pos

}



JI.cursorToLineEnd = function() {
    /*
     * Moves to the cursor the end of the line.
     */
    
    var pos = JI.text.indexOf('\n', JI.editel.selectionStart)
    
    if (pos === -1)
        pos = JI.text.length
    
    JI.cursor.indent = pos - JI.text.lastIndexOf('\n', pos - 1) - 1
    
    return pos

}



JI.keyevent = function(event) {
    /*
     * Receives keydown and keyup events from the editarea.
     */
    
    JI.c = event.keyCode
    JI.pos = parseInt(JI.editel.selectionStart, 10)
    
    if (JI.insertmode) {
        if (JI.c === 27)
            JI.insertMode(false)
        else if (String(event.type) === 'keyup' || JI.lastKeyType === 'keydown') 
            JI.refreshDisplay()
        JI.lastKeyType = String(event.type)
        
        return true
    }
    
    if (String(event.type) === 'keyup')
        return false
    
    if (JI.command.mode) {
        switch (JI.c) {
            case 13: // ENTER execute command
                JI.execute(JI.command.input.value)
                JI.commandInput(false)
                return false
            case 27: // ESC exit command-line input
                JI.commandInput(false)
                break
            default:
                break
        }
        return true
    }
    
    switch (JI.c) {
        case 52: // SHIFT+'4' ('$') cursor to end of line
            if (event.shiftKey)
                JI.pos = JI.cursorToLineEnd()
            else
                JI.editbuffer += '4'
            break
        case 54: // SHIFT+'6' ('^') cursor to first nonespace of line
            if (event.shiftKey)
                JI.pos = JI.cursorToLineBegin()
            else
                JI.editbuffer += '6'
            break
        case 59: // Firefox SHIFT+';' (':') enter command-line input
        case 186: // Chrome SHIFT+';' (':') enter command-line input
            if (event.shiftKey)
                JI.commandInput(true)
            return false
        case 65: // 'a' append
            JI.pos = (event.shiftKey) ? JI.cursorToLineEnd() : JI.pos + 1
            JI.insertMode(true)
            break
        case 73: // 'i' insert
            JI.pos = (event.shiftKey) ? JI.cursorToLineBegin() : JI.pos
            JI.insertMode(true)
            break
        case 37: // cursor left
        case 72: // 'h' cursor left
            if (JI.pos > 0) {
                JI.pos--
                JI.cursor.indent--
            }
            JI.editbuffer = ''
            break
        case 40: // cursor down
        case 74: // 'j' cursor down
            JI.a = JI.text.indexOf('\n', JI.pos) + 1
            JI.b = JI.text.indexOf('\n', JI.a)
            if (JI.a !== 0) {
                JI.a += JI.cursor.indent
                JI.pos = (JI.a < JI.b || JI.b === -1) ? JI.a : JI.b
            }
            JI.editbuffer = ''
            break
        case 38: // cursor up
        case 75: // 'k' cursor up
            if (JI.pos - JI.cursor.indent === 0)
                return false
            JI.a = JI.text.lastIndexOf('\n', JI.pos - 1)
            if (JI.a !== -1) {
                JI.b = JI.text.lastIndexOf('\n', JI.a - 1) + 1
                JI.pos = (JI.a - JI.b < JI.cursor.indent) ? JI.a : JI.b + JI.cursor.indent
            }
            JI.editbuffer = ''
            break
        case 39: // cursor right
        case 76: // 'l' cursor right
            if (JI.pos < JI.text.length) {
                JI.pos++
                JI.cursor.indent++
            }
            JI.editbuffer = ''
            break
        case 68: // 'd' delete line if editbuffer ends with d
            if (/d$/g.test(JI.editbuffer)) {
                JI.a = JI.editCount('d')
                while (JI.a-- > 0) {
                    JI.b = JI.text.lastIndexOf('\n', JI.pos - 1) + 1
                    JI.c = JI.text.indexOf('\n', JI.pos) + 1
                    if (JI.c === 0 && JI.b > 0) JI.b--
                    JI.editel.value = JI.text.slice(0, JI.b)
                    if (JI.c > 0)
                        JI.editel.value += JI.text.slice(JI.c)
                    JI.editel.selectionStart = JI.editel.selectionEnd = JI.b
                    JI.refreshDisplay()
                }
                JI.editbuffer = ''
            } else {
                JI.editbuffer += 'd'
            }
            break;
        case 79: // 'o' line append or line insert if SHIFT+
            JI.editbuffer += 'o'
            JI.a = JI.editCount('o')
            while (JI.a-- > 0) {
                if (event.shiftKey) {
                    JI.pos = JI.text.lastIndexOf('\n', JI.pos - 1) + 1
                } else {
                    JI.pos = JI.text.indexOf('\n', JI.pos) + 1
                    if (JI.pos === 0) JI.pos = JI.text.length + 1
                }
                JI.editel.value = JI.text.slice(0, JI.pos) + '\n' + JI.text.slice(JI.pos)
                JI.editel.selectionStart = JI.editel.selectionEnd = JI.pos
                JI.refreshDisplay()
            }
            JI.insertMode(true)
            break
        case 88: // 'x' delete character if editbuffer ends with d
            JI.editbuffer += 'x'
            JI.a = JI.editCount('x')
            while (JI.a-- > 0) {
                if (JI.pos === JI.text.length)
                    JI.pos--
                JI.editel.value = JI.text.slice(0, JI.pos) + JI.text.slice(JI.pos + 1)
                JI.editel.selectionStart = JI.editel.selectionEnd = JI.pos
                JI.refreshDisplay()
            }
            JI.editbuffer = ''
            break
        default:
            JI.editbuffer += String.fromCharCode(JI.c)
            break
    }
    
    JI.editel.selectionStart = JI.editel.selectionEnd = JI.pos
    JI.refreshCursor()

    return false

}



JI.updateFont = function() {

    document.body.style['font-size'] = JI.font.size + 'pt'
    JI.editel.style['font-size'] = JI.font.size + 'pt'
    JI.command.input.style['font-size'] = JI.font.size + 'pt'
    JI.cursor.el.style['font-size'] = JI.font.size + 'pt'
    
    JI.font.width = JI.cursor.el.offsetWidth
    JI.font.height = JI.cursor.el.offsetHeight
    
    JI.refreshCursor()
    JI.text = null
    JI.refreshDisplay()

}




JI.container.innerHTML = [
    '<table id="JI-frame">',
    '  <tbody>',
    '    <tr><td id="JI-ctrlbar" colspan="2"></td></tr>',
    '    <tr><td id="JI-ruler" valign="top"></td><td valign="top"><div id="JI-body"></div></td></tr>',
    '  </tbody>',
    '</table>'
].join('\n')



// control bar
JI.ctrl.el = document.getElementById('JI-ctrlbar')
JI.ctrl.saveFile = BLACKSMITH.element(JI.ctrl.el, 'span', ['&#x270e;'])
JI.ctrl.replace = BLACKSMITH.element(JI.ctrl.el, 'span', ['a&#8618;b'])
JI.ctrl.increaseFont = BLACKSMITH.element(JI.ctrl.el, 'span', ['A&#8314;'])
JI.ctrl.decreaseFont = BLACKSMITH.element(JI.ctrl.el, 'span', ['a&#8315;'])
JI.ctrl.style = BLACKSMITH.element(JI.ctrl.el, 'span', 'JI-ctrl-highlight', ['&#9775;'])


// command bar
JI.command.el = BLACKSMITH.element(JI.container, 'div', 'ji_cmd_bar', [
    JI.command.input = BLACKSMITH.element('input', 'ji_cmd_input', {'type':'text'}),
    JI.command.info = BLACKSMITH.element('span', 'ji_cmd_info')
])



JI.body = document.getElementById('JI-body')
JI.ruler.el = document.getElementById('JI-ruler')
JI.editel = BLACKSMITH.element(JI.body, 'textarea', 'JI-edit')
JI.underlay.el = BLACKSMITH.element(JI.body, 'div', 'JI-underlay')
JI.cursor.el = BLACKSMITH.element(JI.body, 'span', 'JI-cursor', [' '])
JI.underlay.operator.el = BLACKSMITH.element(JI.underlay.el, 'div', 'JI-underlay')
JI.underlay.whitespace.el = BLACKSMITH.element(JI.underlay.el, 'div', 'JI-underlay')
JI.underlay.keyword.el = BLACKSMITH.element(JI.underlay.el, 'div', 'JI-underlay')
JI.underlay.name.el = BLACKSMITH.element(JI.underlay.el, 'div', 'JI-underlay')
JI.underlay.number.el = BLACKSMITH.element(JI.underlay.el, 'div', 'JI-underlay')
JI.underlay.string.el = BLACKSMITH.element(JI.underlay.el, 'div', 'JI-underlay')
JI.underlay.comment.el = BLACKSMITH.element(JI.underlay.el, 'div', 'JI-underlay')





// JI.container style
JI.container.style.position = 'relative'
JI.container.style.zIndex = '0'
JI.container.style.width = '100%'
JI.container.style.height = '100%'
JI.container.style.margin = '0'
JI.container.style.background = JI.colours.background
JI.container.style.overflow = 'auto'
document.onclick = JI.container.onclick = function(){JI.editel.focus()}



// refresh the display when clicked or window resize
JI.editel.onclick = window.onresize = JI.refreshDisplay



JI.ctrl.saveFile.onclick = function() {
    
    if (JI.ctrl.saveFile.style.opacity === '0.2')
        return
    
    var chmode = JI.insertmode
    
    if (chmode)
        JI.insertMode(false)
    
    JI.commandInput(true)
    JI.command.input.value = ':w'
    JI.save(function(m) {
        if (m.hasOwnProperty('success') && chmode)
            JI.insertMode(true)
        JI.commandInput(false)
    })
    
}


JI.ctrl.replace.onclick = JI.replace


JI.ctrl.increaseFont.onclick = function() {
    JI.font.size++
    JI.updateFont()
}


JI.ctrl.decreaseFont.onclick = function() {
    JI.font.size--
    JI.updateFont()
}


JI.ctrl.style.onclick = function() {
    
    JI.current_style = !JI.current_style
    JI.colours = JI.colour_styles[JI.current_style]
    
    JI.ctrl.style.className = JI.current_style ? 'JI-ctrl-highlight' : ''

    JI.ctrl.el.style.background = JI.colours.ctrlbarbg
    JI.command.el.style.background = JI.colours.cmdbarbg
    JI.ruler.el.style.color = JI.colours.linenumber
    JI.ruler.el.style.background = JI.colours.ruler
    JI.ruler.el.style.borderRight = '1px solid ' + JI.colours.lineborder
    JI.cursor.el.style.borderLeft = '2px solid ' + JI.colours.cursorInsert
    JI.cursor.el.style.background = JI.colours.cursorEdit
    JI.container.style.background = JI.colours.background
    JI.underlay.operator.el.style.color = JI.colours.operator
    JI.underlay.whitespace.el.style.color = JI.colours.whitespace
    JI.underlay.keyword.el.style.color = JI.colours.keyword
    JI.underlay.name.el.style.color = JI.colours.name
    JI.underlay.number.el.style.color = JI.colours.number
    JI.underlay.string.el.style.color = JI.colours.string
    JI.underlay.comment.el.style.color = JI.colours.comment

    JI.text = null
    JI.tokens = []
    JI.refreshDisplay()
    
}


JI.editel.onkeydown = JI.editel.onkeyup = JI.keyevent
JI.command.input.onkeydown = JI.editel.onkeyup = JI.keyevent




BLACKSMITH.element(document.head, 'style', {'type': 'text/css'}, [
    'body {',
    '    font-family: "Monaco", "Menlo", "Ubuntu Mono", "Droid Sans Mono", "Consolas", monospace;',
    '    font-size  : ' + JI.font.size + 'pt;',
    '}',
    '#JI-frame {',
    '    margin-top: 1.1em;',
    '}',
    '.ji_cmd_bar {',
    '    position  : fixed;',
    '    background: ' + JI.colours.cmdbarbg + ';',
    '    right     : 0;',
    '    bottom    : 0;',
    '    visibility: hidden;',
    '    z-index   : 3;',
    '}',
    '#JI-ctrlbar {',
    '    position  : fixed;',
    '    left      : 0;',
    '    right     : 0;',
    '    top       : 0;',
    '    z-index   : 3;',
    '    background   : ' + JI.colours.cmdbarbg + ';',
    '    border-bottom: #555555;',
    '}',
    '#JI-ctrlbar span {',
    '  color : ' + JI.colours.bartext + ';',
    '  margin-left: 5px;',
    '  padding-top: 5px;',
    '  padding-left: 8px;',
    '  padding-right: 8px;',
    '  background   : ' + JI.colours.cmdbarbg + ';',
    '}',
    '#JI-ctrlbar .JI-ctrl-highlight {',
    '  color: #e06c1f;',
    '}',
    '#JI-ctrlbar span:hover, span:focus {',
    '  color: #e06c1f;',
    '  cursor: pointer;',
    '}',
    '.ji_cmd_input {',
    '    background : none;',
    '    border     : none;',
    '    font-family: "Monaco", "Menlo", "Ubuntu Mono", "Droid Sans Mono", "Consolas", monospace;',
    '    font-size  : ' + JI.font.size + 'pt;',
    '}',
    '.ji_cmd_info {',
    '    white-space: pre;',
    '    padding    : 4px;',
    '}',
    '#JI-ruler {',
    '    white-space  : pre;',
    '    color        : ' + JI.colours.linenumber + ';',
    '    text-align   : right;',
    '    padding-left : 5px;',
    '    padding-right: 5px;',
    '    background   : ' + JI.colours.ruler + ';',
    '    width        : 2em;',
    '    border-right : 1px solid ' + JI.colours.lineborder + ';',
    '}',
    '.JI-edit {',
    '    position  : absolute;',
    '    z-index   : 2;',
    '    background: none;',
    '    color     : transparent;',
    '    border    : none;',
    '    outline   : none;',
    '    padding   : 0;',
    '    margin    : 0;',
    '    overflow  : hidden;',
    '    resize    : none;',
    '    font-family: "Monaco", "Menlo", "Ubuntu Mono", "Droid Sans Mono", "Consolas", monospace;',
    '    font-size  : ' + JI.font.size + 'pt;',
    '}',
    '#JI-body {',
    '    position   : relative;',
    '}',
    '.JI-underlay {',
    '    position   : absolute;',
    '    top        : 0;',
    '    white-space: pre;',
    '    z-index   : 1;',
    '    background: none;',
    '}',
    '.JI-cursor {',
    '    position   : absolute;',
    '    white-space: pre;',
    '    z-index    : 1;',
    '    border-left: 2px solid ' + JI.colours.cursorInsert + ';',
    '    font-family: "Monaco", "Menlo", "Ubuntu Mono", "Droid Sans Mono", "Consolas", monospace;',
    '    font-size  : ' + JI.font.size + 'pt;',
    '}'
])


JI.text = ''
while (JI.text.length < 1000) {
    JI.underlay.gap.push(JI.text)
    JI.text += ' '
}

JI.updateFont()
JI.ctrl.style.onclick()
JI.editel.focus()
JI.insertMode(false)

BLACKSMITH.argv.forEach(function(arg) {
    (arg[0]==='f' && JI.open(arg[1]))
})

